package com.wt.admin.config.aspect;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.aizuda.easy.security.domain.Rep;
import com.aizuda.easy.security.domain.Req;
import com.wt.admin.config.aspect.annotation.LogAno;
import com.wt.admin.domain.entity.sys.SysOperationLogEntity;
import com.wt.admin.domain.vo.sys.UserVO;
import com.wt.admin.service.sys.SysOperationLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Resource
    private SysOperationLogService sysOperationLogService;
    private final BlockingQueue<SysOperationLogEntity> logQueue = new LinkedBlockingQueue<>();
    private final SysOperationLogEntity logEntity = new SysOperationLogEntity();


    @PostConstruct
    public void init(){
        consume();
    }

    /**
     * 操作日志 人，时间，ip，方法名，参数，返回值，执行时间
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("execution(* com.wt.admin.controller..*(..))")
    public Object Around(ProceedingJoinPoint point) throws Throwable {
        // 获取request
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        String requestUrl = request.getRequestURL().toString();
        long startTime = System.currentTimeMillis();
        Object result;
        // 执行方法
        MethodSignature method = (MethodSignature) point.getSignature();
        LogAno logAno = method.getMethod().getDeclaredAnnotation(LogAno.class);
        result = point.proceed();
        if(ObjectUtil.isEmpty(logAno)){
            return result;
        }
        produce(startTime,logAno.name(),method.getMethod().getName(),requestUrl,point.getArgs(),result);
        return result;
    }

    private void produce(long startTime,String name,String method,String url,Object[] args,Object result) throws InterruptedException {
        Req<Object, UserVO> objectUserVOReq = analysisArgs(args);
        SysOperationLogEntity clone = logEntity.clone();
        clone.setUserId(ObjectUtil.isEmpty(objectUserVOReq.getUser())?
                null: objectUserVOReq.getUser().getId());
        clone.setLogName(name);
        clone.setLogMethod(method);
        clone.setLogPath(url);
        clone.setLogParam(objectUserVOReq.getData());
        clone.setLogResultCode(JSONUtil.toBean(JSONUtil.toJsonStr(result), Rep.class).getCode());
        clone.setCreateTime(DateUtil.now());
        long endTime = System.currentTimeMillis();
        clone.setLogTime((int) (endTime - startTime));
        logQueue.put(clone);
    }


    private void consume(){
        Thread thread = new Thread(() -> {
            while (true){
                try {
                    ThreadUtil.sleep(5000);
                    sysOperationLogService.add(logQueue.stream().toList());
                    logQueue.clear();
                }catch (Exception e){
                    log.error("操作日志错误 :{}",e);
                    ThreadUtil.sleep(1000);
                }
            }
        });
        thread.setName("操作日志消费");
        thread.start();
    }

    /**
     * 解析参数
     * @param args
     */
    private Req<Object,UserVO> analysisArgs(Object[] args){
        Req<Object,UserVO> req = null;
        for (int i = 0; i < args.length; i++) {
            if(! (args[0] instanceof Req<?,?>)){
                continue;
            }
            req = (Req<Object, UserVO>) args[0];
            return req;
        }
        return req;
    }

}
